#include <klib.h>
#include <types.h>
#include <globals.h>
#include <hardware.h>
#include <i386_arch.h>
#include <pic.h>
#include <pit.h>
//#include <k_func.h>
#include <string.h>
#include <multiboot.h>
#include <spinlock.h>
#include <panic.h>
#include <scheduler.h>

#include <malloc.h>
#include <stuffer.h>

// Managers
#include <vmm.h>
#include <kbd.h>
#include <tty.h>
#include <hdd.h>

#include <linkedlist.h>

/* this will be placed somewhere else */
//#define NULL_PROCESS_PID     0

/* Frequency in Hz */
#define FREQUENCY            100

/*
#define INIT_PROCESS_PID     1
#define INIT_PROCESS_IDX     0

#define MAX_PROCESSES        3
#define PROC_STACK_SIZE   1024
*/


#define interrupt(num)     asm volatile ("int %0"::"i"(num))

///// cpu-- //

/*
#define WAIT_SECOND() do{ \
	int j0, j1; \
	j0 = inb(0x71); \
	j1 = j0; \
	while(j1 == j0) { \
		 j1 = inb(0x71); \
	} \
        } while(0)


#define rdtsc(low,high) \
     __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
*/


///// --cpu //


//typedef unsigned long pid_t;

/*
typedef struct {
  TSS_T tss;
  pid_t pid;
  word_t tss_desc;
  byte_t stack[PROC_STACK_SIZE];
} Process_T;

Process_T procs[MAX_PROCESSES];
*/

unsigned int MemMax=1;

//byte_t kernel_stack[KERNEL_STACK];

/*
void setup_IDT_Traps(void);
void setup_IDT_Interrupts(void);
void setup_scheduler(unsigned short freq);
void remake_gdt(void);
void prepare_TSSs(void);
*/

/* isrs */
/*
void test_trap(void);
void test_intr(void);
*/

//extern dword_t *GRUB_boot_header;


/* from low_level.asm */
extern void issue_iret(void);
extern void asm_run(void);

extern int pci_list(void);
extern void print_allocations();

int MemoryProbe(void);
void areste(void);
void cpu_speed(void);

int linkedlist_test();

/*
extern void unmap_temp(void);
*/

extern void print_image_list();
extern void start_multitasking(void);

void pit_isr2(void)
  {
    static int val=0;

    //printf("PIT!! %d\n",val);
    if (val % FREQUENCY == 0)
      printf("One second!\n");

    val++;
  }

/*
void show_message(process_t *p)
{
  printf("MESSAGE:\n");
  printf("Current process name: %s\n",p->name);
  printf("Current KSTACK   top: %x\n",p->kstack_top);
  printf("========================\n");
}
*/
void show_message(dword_t data)
{
  printf("MESSAGE:\n");
  printf("EAX: 0x%x\n",data);
  printf("========================\n");
}


void k_main(void)
  {
    //IDT_Entry_T tte;
    //char *bb;
    word_t *mmap_addr;
    struct SysMemMap_T *mmap; 
    unsigned int i,mmap_len;
    spinlock_t spq;
    word_t ch;
    dword_t ogbh;

    char *p,*q;

    dword_t *pd;

    dword_t dd,ms,us;

    ogbh=GRUB_boot_header;

    disable_interrupts();
    reprogram_PICs();    
    globalVars_Initialize();      /* initialize global variables */
    parse_multi_boot_structure(); /* parse multiboot structure passed by GRUB */
    
    stuffer_initialize();         /* load stuffer archive and store it in a safe place */

    //MemMax=MemoryProbe();       /* Probe amount of RAM */
    virtual_init();               /* Initialize memory manager and paging */    


    prepare_GDT();                /* GDT with base 0 and limit 4 GiB */
    prepare_IDT();                /* IDT with Intel exceptions, IRQs and syscall() */
    prepare_TSS();                /* Make an empty TSS and load task register */


    // free memory that was mapped 1:1
    // before doing this, we need to change the putchar() video memory address...
    virtual_post_init();
    // remap video memory page 
    remap_video_memory();

    // initialize tty driver
    tty_initialize();

    clear();                    /* clear screen */
    

    // Welcome message
    printf("Horizon System I (RealTime)\n");    
    printf("Welcome.\n");    


    //setup_IDT_Traps();            /* Setup Traps (generated by CPU) */



    /*
    printf("\n\nKernel Start = %x\n",Kernel_Start);
    printf("Kernel End = %x\n",Kernel_End);
    printf("\n\nKernel = %d\n",Kernel_DataEnd-Kernel_Start);
    */    

    
    //bb=0x1800000;
    //bb=0xC00000;
    //bb=20*1024*1024;
    //*bb=1;
    
    //asm_run();

    //printf("Am I still alive?\n");    
    //areste();
    

    //printf("\n\nRAM size: %d MB\n",MemoryProbe());
    //printf("\n\nRAM size: %d MB\n",MemMax);


    /*
    printf("\n\nGRUB Mem Lower: %d KB\n",*(GRUB_boot_header+1)+1);
    printf("\n\nGRUB Mem Upper: %d KB\n",*(GRUB_boot_header+2)+1024);
    */
    

    //cpu_speed();


    //printf("GRUB boot header: 0x%x\n",ogbh);
    printf("GRUB Mem Lower: %d KB\n",mem_lower);
    printf("GRUB Mem Upper: %d KB\n",mem_upper);
    if (mmap_valid)
      printf("Memory map is valid as well.\n\n");

    printf("\nGRUB RAM size: %d KB\n",mem_tot);


      // we have to do this before enabling paging...
    
    
    /*
    if (mmap_valid)
      {	
	struct Multiboot_Info_T *mbi;
	struct SysMemMap_T *mmap;

	GRUB_boot_header=ogbh+4;
	mbi=(struct Multiboot_Info_T *)GRUB_boot_header;

	for (i=0;i<mbi->mmap_length;i+=mmap->size+sizeof(mmap->size))
	  {
	    //calculate current entry
	    mmap=(struct SysMemMap_T *)((unsigned long)mbi->mmap_addr+i);
	    
	    printf (" size = 0x%x, base_addr = 0x%x,"
		    " length = 0x%x, type = 0x%x\n",
		    (unsigned) mmap->size,
		    (unsigned) mmap->base_addr_high,
		    (unsigned) mmap->base_addr_low,
		    (unsigned) mmap->bytes_high,
		    (unsigned) mmap->bytes_low,
		    (unsigned) mmap->type);
	  }
      }
    */

    

    //printf("Patching GRUB_boot_header...\n");
    //printf("Before patch = 0x%x\n",GRUB_boot_header);
    //GRUB_boot_header=ogbh+4;
    //printf("After patch = 0x%x\n",GRUB_boot_header);

    /*
    if (1)
      {
	// Are mods_* valid? 
	struct Multiboot_Info_T *mbi;
	

	mbi=(struct Multiboot_Info_T *)GRUB_boot_header;
	
	printf("GRUB Modules:\n");
	if (CHECK_FLAG(mbi->flags,3))
	  {
	    struct ModulesList_T *mod;
	    unsigned int i;
	    
	    printf ("mods_count = %d, mods_addr = 0x%x\n",(int) mbi->mods_count, (int) mbi->mods_addr);
	    mod = (struct ModulesList_T *) (mbi->mods_addr + RELOC_PATCH);
	    for (i = 0; i < mbi->mods_count; i++)
	      {
		printf (" mod_start = 0x%x, mod_end = 0x%x, string = %s\n",(unsigned) mod->mod_start,(unsigned) mod->mod_end,(char *) mod->string);
		mod++;
	      }
	  }
      }

    printf("Halting...");
    halt();
    */

    //printf("*** Kernel SIZE = %d\n",Kernel_End - Kernel_Start +1);
    //printf("Stuffer archive status: %d\n",stuffer_get_status());
    stuffer_show_information();

    // build list of files
    stuffer_make_process_list();
    
    // show extracted information
    print_image_list();

    initialize_scheduler();
    

    //printf("Halting here...");
    //printf("Going multitasking...\n");
    //halt();
    //    asm("int $0x03");    

    /*
    p=(char *)malloc(12*sizeof(char));
    *p='M';
    q=(char *)malloc(50*sizeof(char));
    //q=(char *)malloc(5*1024*1024*sizeof(char));
    *q='H';
    free(p);
    print_allocations();
    p=(char *)malloc(15*sizeof(char));
    *p='Y';
    free(p);
    free(q);
    p=(char *)malloc(5*sizeof(char));
    *p='J';
    free(p);    
    */

    //linkedlist_test();

    /*
    printf("System halted.\n");
    halt();
    */

    
    /*
    // try to cause a page fault
    p=0xDEADBEEF;
    *p='Q';
    */
    

    /*
    spinlock_init(&spq);
    printf("before locking: spq=%d\n",spq);

    test_and_set(1,&spq);
    printf("after locking: spq=%d\n",spq);
    */


    /*
    irq_enable(1);
    enable_interrupts();
    */

    /*
    kbdKeyboard_Initialize();
    enable_interrupts();
    //interrupt(0x31);
    //for (;;);
    */

    /*
    kbd_initialize();
    enable_interrupts();
    for (;;);
    */

    /*
    // Test keyboard
    for (;;)
      {
	ch=kbd_read_key();
	printf("Scancode: %x\n",ch);
      }
    */

    /*
    // PRINT PAGE DIRECTORY
    printf("Page Directory:\n");
    //pd=(dword_t *)((unsigned long)0xFFFFFFFF-0x1000);

    // Top 4 MB start at: 0xFFC00000
    // Top 4 KB start at: 0xFFFFF000

    
    //pd=(dword_t *)((unsigned long)0xFFE00000);
    //pd=(dword_t *)((unsigned long)0xFFFFF000);
    //pd=(dword_t *)PAGE_DIR_START;
    for (i=0;i<1024;i+=64)
      {
	//pd=(dword_t *)((unsigned long)0xFFFFF000);
	pd=(dword_t *)PAGE_DIR_START;
	pd+=i;
	printf("Entry %d: M[%x] = %x\n",i,pd,*pd);
      }


    //pd=(dword_t *)((unsigned long)0xFFFFF000);
    pd=(dword_t *)PAGE_DIR_START;
    pd+=1023;
    printf("*** PDBR Entry %d: M[%x] = %x\n",1023,pd,*pd);
    */

    /*
    pd=(dword_t *)((unsigned long)0xFFE00000);

    for (i=0;i<15;i++)
      {
	printf("Entry %d: M[%x] = %x\n",i,pd,*pd);
	pd++;
      }
    */


    // Page table

    /*
    printf("System halted.\n");
    halt();
    */


    //pit_set_frequency(FREQUENCY);
    //pit_set_frequency(100);
    //assign_IRQ(0,(dword_t)&pit_isr);

    //starting multitasking
    start_multitasking();

    // THIS IS THE KEYBOARD ONE
    /*
    kbd_initialize();
    enable_interrupts();

    for(;;)
	kbd_read_key();		
    */


    /*
    for(;;)
      {

	pit_get_time(&dd,&ms,&us);
	printf("Time: dd=%d ss=%d ms=%d us=%d\n",dd,ms/1000,ms,us);


	  //kbd_read_key();
	
      }
    */

    printf("Looping forever...\n");
    for(;;);

    printf("System halted.\n");
    halt();

    
    p=(char *)malloc(1024*sizeof(char));

    // Clear buffer
    for (i=0;i<1024;i++)
      *(p+i)='\0';

    strcpy(p,"Claudette!");

    /*
    printf("Writing sector... ");
    hdd_write_sector(0,8,1,p);
    printf("done.\n");

    // Clear buffer
    for (i=0;i<1024;i++)
      *(p+i)='\0';
    
    printf("Reading sector... ");
    hdd_read_sector(0,8,1,p);
    printf("done.\n");
    */

    printf("Contents:\n");
    printf("==================================================\n");
    memory_dump(p,128);
    printf("==================================================\n");

    free(p);
      

    pci_list();


    //asm volatile ("int $0x31");
    //interrupt(0x25);
    //asm volatile ("int $0x31");

    //printf("Alive aloo!!!\n");

    printf("System halted.\n");
    halt();
  }

int MemoryProbe(void)
  {
    word_t *ptr;
    word_t oval,val=0xAA55;
    int good,ct;


    ct=1;
    good=1;

    do {
      /* 
       * Skip known ISA memory hole.
       * Our memory mapper will ignore this MB anyway... so it doesn't matter
       * anyway it's a waste, I know. I'll fix this sometime.
       */	 
      if (ct==15)  // if ptr==15MB then ptr=16MB	
	ct=16;

      //Calculate the memory address to write to
      ptr=(word_t *)(ct*0x100000);  // ct*1 MB
      
      oval=*ptr;        // Save original value      
      *ptr=val;         // Write a test value to memory
    
      if (*ptr!=val)    // if the value is not equal to the one written
	good=0;         // then we've reached the maximum amout of memory
      else
	ct++;           // try with next MB

      *ptr=oval;        // restore original value
    } while(good);

    return ct;   
  }


void areste(void)
{
  asm("int $0");
  printf("Areste!!\n");
}


// CPU Speed Detection 


static inline unsigned long long read_tsc(void)
{
	unsigned long low, high;

	//rdtsc(low, high);
	return (((unsigned long long) high << 32) | low); 
}

void cpu_speed(void)
{
	unsigned long khz, ticks;
	unsigned long long prev, curr;

	printf("Detect CPU speed... ");

	outb(0x70, 0);
	//asm volatile ("outb %%al,%%dx": :"d" (0x70), "a" (0));

	// examples
	//asm volatile ("inb %%dx,%%al":"=a" (ret):"d" (port)); 
	//asm volatile ("outb %%al,%%dx": :"d" (port), "a" (value));


	//WAIT_SECOND();
	prev = read_tsc();
	//WAIT_SECOND();
	curr = read_tsc();

	ticks = curr - prev;

	khz = ticks / 1000;
	printf("%d.%d MHz\n", khz / 1000, khz%1000);
	//printf("Ticks: %d\n", ticks);
}


int compare_items(void *p1,void *p2)
{
  int *n1,*n2;

  n1=(int *)p1;
  n2=(int *)p2;

  return *n1-*n2;
}

int linkedlist_test()
{
  int i,*num,*mum;
  ptr_list_t list;
  ptr_iterator_t iter;

  list=llist_make_list();
  llist_set_comparator(list,compare_items);

  for (i=0;i<10;i++)
    {
      num=(int *)malloc(sizeof(int));
      *num=i;
      llist_add_first(list,num);
    }

  
  iter=llist_get_nodes(list);

  while (llist_iter_has_next(iter))
    {
      num=llist_iter_next(iter);
      printf("Priting: %d\n",*num);
    }

  mum=(int *)malloc(sizeof(int));
  *mum=6;

  /*
  num=search(list,mum);
  *mum=1;
  printf("Found: %d\n",*num);
  */

  llist_remove(list,mum);

  free(iter);
  iter=llist_get_nodes(list);

  while (llist_iter_has_next(iter))
    {
      num=llist_iter_next(iter);
      printf("Priting: %d\n",*num);
    }
  

  free(iter);

  return 0;
}
